#ifndef ENCODERNC_H
#define ENCODERNC_H

#include <newcode/ByteMatrix.h>
#include <zxing/common/Array.h>
#include <zxing/common/Str.h>

namespace newcode {
namespace encoder {
using namespace std;
using namespace zxing;

enum EncodeMode
{
    ENCODE_MODE_2_LEVELS,
    ENCODE_MODE_4_LEVELS,
    ENCODE_MODE_8_LEVELS,
    ENCODE_MODE_3_LEVELS
};

class Encoder
{
private:
    const static float ERROR_CORECTION_LEVELS[];
    const static unsigned char MASK = 0xA8;
    const static int FETCH_MASKS[];

    ArrayRef<unsigned char> data;
    EncodeMode encodeMode;
    ArrayRef<unsigned char> formatBytes;
    int sizeInBarsels;
    int initialDataSize;
    bool dataIsString;
    size_t pointer;

    //getNextElement3Levels() state variables.
    vector<int> digits;
    bool currentStateOdd;

public:
    Encoder();
    Ref<ByteMatrix> encode(String data,
        EncodeMode encodeMode = ENCODE_MODE_4_LEVELS);
    Ref<ByteMatrix> encode(ArrayRef<unsigned char> data,
        EncodeMode encodeMode = ENCODE_MODE_4_LEVELS);

private:
    inline int messageSize();
    void computeSizeInBarsels();
    void setupFormatData();
    Ref<ByteMatrix> buidMatrix();
    inline ArrayRef<unsigned char> getFormatElement(int index);
    inline ArrayRef<unsigned char> getNextElement();
    ArrayRef<unsigned char> getNextElement3Levels();
    void setBarsels(Ref<ByteMatrix> matrix, int h, int w,
        ArrayRef<unsigned char> barsels);
    inline unsigned char getNextMaskedByte();
    unsigned char barselForValue(int value) const;
    inline float positiveRound(float value) const;
    ArrayRef<unsigned char> addErrorCorrectionBytes(
        const ArrayRef<unsigned char> initialData);
    static vector<int> toTernarSystem(int val);
};

}
}

#endif // ENCODERNC_H
